/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-04
 * V4.0
 */
package com.jphenix.kernel.baseobject.instanceb;

import com.jphenix.driver.json.Json;
import com.jphenix.driver.log.FLog;
import com.jphenix.driver.propertie.instanceb.XmlConfProp;
import com.jphenix.driver.threadpool.ThreadSession;
import com.jphenix.kernel.objectloader.instanceb.BaseBean;
import com.jphenix.kernel.objectloader.interfaceclass.IBean;
import com.jphenix.kernel.objectloader.interfaceclass.IBeanFactory;
import com.jphenix.share.bean.instancea.FilesUtil;
import com.jphenix.share.lang.*;
import com.jphenix.share.tools.Base64;
import com.jphenix.share.tools.FileCopyTools;
import com.jphenix.share.tools.HttpCall;
import com.jphenix.share.tools.MD5;
import com.jphenix.share.util.ClassUtil;
import com.jphenix.share.util.SFilesUtil;
import com.jphenix.share.util.StringUtil;
import com.jphenix.standard.beans.IBase;
import com.jphenix.standard.beans.IFilesUtil;
import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.internationalization.ILanguage;
import com.jphenix.standard.internationalization.ILanguageManager;
import com.jphenix.standard.log.ILog;
import com.jphenix.standard.servlet.IActionContext;
import com.jphenix.standard.viewhandler.IViewHandler;

import java.io.File;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 所有类的基类
 * 该类中的方法都是独立的,没有引用其它类
 * 注意：language类不能跟日志类实例一样直接使用，而是需要通过 getLanguage()方法获取
 * 
 * 2018-05-28 增加了thinDate() 返回yyyymmdd格式日期的字符串，通常用在建立日期值文件夹等场合
 * 2018-07-03 增加了获取当前登录的用户数据主键，或用户姓名方法
 * 2018-07-05 增加了强制转换字符串后再去掉左右空格 strm()
 * 2018-07-18 修改了日志输出类，增加输出临时日志方法
 * 2018-07-30 增加了输出自定义日志文件的日志内容
 * 2018-07-31 增加了longs ints 将对象强制转换成数字字符串
 * 2018-08-08 增加了从线程会话中移除指定参数值
 * 2018-09-05 beanFactory.getConfProp()方法无需用IBeanFactoryManager接口强制转换
 * 2018-09-13 简化了整个架构中日志初始化步骤
 * 2818-12-07 增加了返回临时文件根路径（全路径）
 * 2019-01-02 增加了创建临时文件方法（返回文件对象）
 * 2019-01-10 path方法，如果传入空字符串，返回网站根路径（之前返回WEB-INF绝对路径）
 * 2019-01-18 修改了setBase方法，支持将类加载器直接设置进来
 * 2019-02-01 去掉了是否输出错误日志方法，只有一个日志开关outLog
 * 2019-02-22 增加了内部（自开发项目）base64加解密方法 ne64() nd64()
 * 2019-03-15 修改了输入日志到自定义文件的方法参数顺序
 * 2019-05-14 增加了cutDou方法，截取小数点位数，不做四舍五入
 * 2019-05-15 增加了返回带格式的double类型字符。格式：xx,xxx,xxx,xxx.xxxxx
 * 2019-06-11 增加了返回当前毫秒值方法
 * 2019-06-13 修改了Base64解码方法
 * 2019-07-11 增加了douStr(Object)方法
 * 2019-07-15 修改了上传文件根文件夹为可配置路径
 *            增加了返回资源（上传、下载、备份）根文件夹file_sources的绝对路径 sourcePath(subPath)
 *            增加了返回相对于上传文件根路径的绝对路径 uploadPath(subPath)
 * 2019-07-16 增加了从配置文件中获取参数的方法，带默认值参数
 * 2019-12-21 增加了获取指定临时根路径的相对路径的绝对路径
 * 2020-01-13 增加了p64方法，做Base64编码，并做URLEncode
 * 2020-04-03 增加了ue64方法，Base64编码，编码后再做URL格式编码
 * 2020-08-06 修改了配置文件类，不再使用接口，直接使用实体类
 * 2020-08-26 将日志输出传入Exception改为Throwable
 * 2021-03-15 整理了一下代码格式
 * 2021-09-11 日志输出方法中，增加了日志提示标题入参
 * 
 * @author 刘虻
 * 2006-10-23下午02:33:20
 */
@ClassInfo({"2021-09-11 17:43","所有类的基类"})
public class ABase extends BaseBean implements IBase {

	/**
	 * 受类加载器管理
	 */
	final static String BEAN = "1";

	//日志类
	protected ILog             log                      = null;
	//获取配置文件管理类
	protected XmlConfProp      prop                     = null;
	//获取语言管理类
	protected ILanguageManager languageManager          = null;
	//文件处理工具
	protected IFilesUtil       filesUtil                = null;
	//禁用语言管理
	private   Boolean          disabledLanguageManager  = null;

	/**
	 * 构造函数
	 * 2006-10-23下午02:33:20
	 */
	public ABase() {
		super();
	}

	/**
	 * 设置基本类信息从目标类
	 * @author 刘虻
	 * 2007-11-7下午05:21:42
	 * @param obj 目标类
	 */
	@Override
    public void setBase(Object obj) {
		if (obj==null) {
			return;
		}
		//类信息
		String[] infos = ClassUtil.getBeanInfo(this.getClass());
		if(infos.length>0) {
			_beanID = infos[0];
		}
		if(obj instanceof IBeanFactory) {
			setBeanFactory((IBeanFactory)obj);
		}else if(obj instanceof IBean) {
			setBeanFactory(((IBase)obj).getBeanFactory());
		}
	}

	/**
	 * 覆盖方法
	 * 刘虻
	 * 2013-4-9 下午4:20:10
	 */
	public ILanguageManager getLanguageManager() {
		if(languageManager==null && disabledLanguageManager==null) {
			if(!getBeanFactory().beanExists(ILanguageManager.class)) {
				disabledLanguageManager = new Boolean(true);
				return null;
			}
			try {
				//从类加载器中获取
				languageManager =
						getBeanFactory()
							.getObject(ILanguageManager.class,this);
				disabledLanguageManager = new Boolean(false);
			}catch(Exception e) {
				e.printStackTrace();
				disabledLanguageManager = new Boolean(true);
			}
		}
		return languageManager;
	}

	/**
	 * 通过浏览器中的语言代码转换为内部语言代码
	 * （有可能多个外部语言代码对应内部同一个语言代码）
	 * 比如 key:zh_cn  value:chs   key小写
	 * @param key 浏览器中的语言代码
	 * @return 内部语言代码
	 * 2017年2月23日
	 * @author MBG
	 */
	public String getLanguageCode(String key) {
		if(disabledLanguageManager==null) {
			getLanguageManager();
			if(languageManager!=null) {
				return str(languageManager.getLanguageCodeMap().get(key));
			}
		}
		if(disabledLanguageManager) {
			return key;
		}
		return str(languageManager.getLanguageCodeMap().get(key));
	}

	
	/**
	 * 覆盖方法
	 * 刘虻
	 * 2010-4-28 下午05:02:53
	 */
	@Override
    public void setBeanFactory(IBeanFactory beanFactory) {
		super.setBeanFactory(beanFactory);
		//构建并设置日志影子类
		log = FLog.getLog(beanFactory.getLogShadow(),this);
		//设置配置文件信息类
		prop = beanFactory.getConfProp();
		filesUtil = new FilesUtil();
		filesUtil.setWebInfPath(beanFactory.getWebInfPath());
		filesUtil.setTempPath(prop.getParameter("TempPath"));
		filesUtil.setUploadBasePath(prop.getParameter("UploadPath"));
	}

	/**
	 * 通过接口获取指定类实例（不抛异常）
	 * @param interfaceCls 接口类
	 * @return 类实例
	 * 2017年1月23日
	 * @author MBG
	 */
	public <T> T bean(Class<T> interfaceCls) {
		try {
			if(!getBeanFactory().beanExists(interfaceCls)) {
				return null;
			}
			return getBeanFactory().getObject(interfaceCls,this);
		}catch(Exception e) {}
		return null;
	}

	/**
	 * 通过类主键获取指定类实例（不抛异常）
	 * @param beanID 类主键
	 * @return 类实例
	 * 2017年1月23日
	 * @author MBG
	 */
	public <T> T bean(String beanID) {
		try {
			if(!getBeanFactory().beanExists(beanID)) {
				return null;
			}
			return getBeanFactory().getObject(beanID,this);
		}catch(Exception e) {}
		return null;
	}

	/**
	 * 指定类是否存在
	 * @param interfaceCls 指定类的接口
	 * @return 是否存在
	 * 2017年3月29日
	 * @author MBG
	 */
	public boolean beanExist(Class<?> interfaceCls) {
		if(interfaceCls==null) {
			return false;
		}
		return getBeanFactory().beanExists(interfaceCls);
	}

	/**
	 * 指定类是否存在
	 * @param beanID 指定类的主键
	 * @return 是否存在
	 * 2017年3月29日
	 * @author MBG
	 */
	public boolean beanExist(String beanID) {
		if(beanID==null || beanID.length()<1) {
			return false;
		}
		return getBeanFactory().beanExists(beanID);
	}

	/**
	 * 通过类接口获取指定类实例
	 * 刘虻
	 * 2010-7-27 下午02:15:36
	 * @param interfaceCls 接口类
	 * @return 指定类实例
	 * @throws Exception 执行发生异常
	 */
	public <T> T getBean(Class<T> interfaceCls) throws Exception {
		return getBeanFactory().getObject(interfaceCls,this);
	}

	/**
	 * 通过类主键获取指定类实例
	 * @param beanID 类主键
	 * @return 指定类实例
	 * @throws Exception 异常
	 * 2016年6月8日
	 * @author MBG
	 */
	public <T> T getBean(String beanID) throws Exception {
		return getBeanFactory().getObject(beanID,this);
	}

	/**
	 * 覆盖方法
	 * 刘虻
	 * 2010-2-2 下午03:20:04
	 */
	public ILanguage getLanguage(String languageCode) {
		return getLanguageManager().getLanguge(this);
	}

	/**
	 * 构建一个新的类，如果这个类的父类也是ABase，则自动设置ABase中的信息
	 * 注意：该类构建函数中没有任何参数
	 * 刘虻
	 * 2011-9-26 上午11:05:47
	 * @param cls 要构建的类
	 * @throws Exception 异常
	 * @return 类实例
	 */
	public Object newBean(Class<?> cls) throws Exception {
		if(cls==null) {
			return null;
		}
		//构建类
		Object res = cls.newInstance();
		if(res instanceof IBase) {
			((IBase)res).setBase(this);
		}
		return res;
	}

	/**
	 * 覆盖方法
	 * 刘虻
	 * 2010-2-2 下午03:20:40
	 */
	public String i(
			String contentKey,String defaultContent,String[] paraValues) {
		if(disabledLanguageManager==null) {
			getLanguageManager();
		}
		if(disabledLanguageManager) {
			return defaultContent;
		}
		return languageManager.getLanguge(this).i(contentKey,defaultContent,paraValues);
	}

	/**
	 * 输出指定的语言信息
	 * 刘虻
	 * 2013-4-7 上午10:41:24
	 * @param contentKey			语言信息主键
	 * @param defaultContent		默认语言
	 * @return	指定的语言信息
	 */
	public String i(String contentKey,String defaultContent) {
		if(disabledLanguageManager==null) {
			getLanguageManager();
		}
		if(disabledLanguageManager) {
			return defaultContent;
		}
		return languageManager.getLanguge(this).i(contentKey,defaultContent,null);
	}

	/**
	 * 输出指定的语言信息
	 * 刘虻
	 * 2013-4-7 上午10:41:24
	 * @param contentKey			语言信息主键
	 * @return	指定的语言信息
	 */
	public String i(String contentKey) {
		if(disabledLanguageManager==null) {
			getLanguageManager();
		}
		if(disabledLanguageManager) {
			return contentKey;
		}
		return languageManager.getLanguge(this).i(contentKey,null,null);
	}

	/**
	 * 输出调试日志（面向程序的日志）
	 * @param content 日志内容
	 * 2014年7月8日
	 * @author 马宝刚
	 */
	public void log(Object content) {
		log.log(content);
	}

	/**
	 * 输出调试日志（面向程序的日志）
   * @param title   日志提示标题
	 * @param content 日志内容
	 * 2014年7月8日
	 * @author 马宝刚
	 */
	public void log(String title,Object content) {
		log.log(title,content);
	}

	/**
	 * 输出自定义文件日志
	 * @param content 日志内容
	 * @param fileKey 自定义日志文件头（注意：需要在日志配置信息中设置允许输出的日志文件头，
	 *                才能输出日志，另外，自定义文件头名字不能是log或者以data_开头的名字）
	 * 2018年7月30日
	 * @author MBG
	 */
	public void olog(String fileKey,Object content) {
		log.olog(content,fileKey);
	}

	/**
	 * 输出自定义文件日志
   * @param title   日志提示标题
	 * @param content 日志内容
	 * @param fileKey 自定义日志文件头（注意：需要在日志配置信息中设置允许输出的日志文件头，
	 *                才能输出日志，另外，自定义文件头名字不能是log或者以data_开头的名字）
	 * 2018年7月30日
	 * @author MBG
	 */
	public void olog(String title,String fileKey,Object content) {
		log.olog(title,content,fileKey);
	}

	/**
	 * 输出临时调试日志（调试后务必删除该日志）
	 * @param content 日志内容
	 * 2018年7月18日
	 * @author MBG
	 */
	public void tlog(Object content) {
		log.tlog(content);
	}

	/**
	 * 输出临时调试日志（调试后务必删除该日志）
   * @param title   日志提示标题
	 * @param content 日志内容
	 * 2018年7月18日
	 * @author MBG
	 */
	public void tlog(String title,Object content) {
		log.tlog(title,content);
	}

	/**
	 * 输出交易数据日志
	 * @param content 交易数据
	 * 2016年6月7日
	 * @author MBG
	 */
	public void dataLog(Object content) {
		log.dataLog(content);
	}

	/**
	 * 输出信息日志（面向业务的日志）
	 * @param content 日志内容
	 * 2014年7月8日
	 * @author 马宝刚
	 */
	public void info(Object content) {
		log.info(content);
	}

	/**
	 * 输出信息日志（面向业务的日志）
   * @param title   日志提示标题
	 * @param content 日志内容
	 * 2014年7月8日
	 * @author 马宝刚
	 */
	public void info(String title,Object content) {
		log.info(title,content);
	}

	/**
	 * 输出警告日志
	 * @param content 日志内容
	 * @param e 异常信息类
	 * 2014年7月8日
	 * @author 马宝刚
	 */
	public void warning(Object content,Throwable e) {
		log.warning(content, e);
	}

	/**
   * 输出警告日志
   * 
   * @param content 日志内容 2014年7月8日
   * @author 马宝刚
   */
  public void warning(Object content) {
    log.warning(content, null);
  }

  /**
   * 输出错误日志
   * @param content 日志内容
   * 2014年7月8日
   * @author 马宝刚
   */
	public void error(Object content) {
    log.error(content,null);
  }

	/**
	 * 输出错误日志
	 * @param content 日志内容
	 * @param e 异常信息类
	 * 2014年7月8日
	 * @author 马宝刚
	 */
	public void error(Object content,Throwable e) {
		log.error(content,e);
	}

	/**
	 * 输出错误日志到页面控制台
	 * 该日志不会输出到系统控制台和文件中
	 * @param content 日志内容
	 * 2015年4月2日
	 * @author 马宝刚
	 */
	public void debug(Object content) {
    log.debug(content);
	}

	/**
	 * 输出错误日志到页面控制台
	 * 该日志不会输出到系统控制台和文件中
   * @param title   日志提示标题
	 * @param content 日志内容
	 * 2015年4月2日
	 * @author 马宝刚
	 */
	public void debug(String title,Object content) {
    log.debug(title,content);
	}

	/**
	 * 将对象强制转换为long型
	 * @param lonObj 待转换的字符串
	 * @return 转换后的long型
	 * 2015年3月13日
	 * @author 马宝刚
	 */
	public long lon(Object lonObj) {
    return SLong.valueOf(lonObj);
	}

	/**
   * 将对象强制转换为long型
   * @param lonObj 待转换的字符串
   * @return 转换后的long型
   * 2015年3月13日
   * @author 马宝刚
   */
	public long slong(Object lonObj) {
    return SLong.valueOf(lonObj);
	}

	/**
	 * 将对象（通常为字符串）转换为日期对象
	 * @param dateObj 待转换的对象
	 * @param format 日期格式（可选）
	 * @return 日期对象
	 * 2015年3月13日
	 * @author 马宝刚
	 */
	public SDate date(Object dateObj,String... format) {
    if(format!=null && format.length>0) {
      return new SDate(SString.valueOf(dateObj),format[0]);
    }
    return new SDate(dateObj);
	}

	/**
	 * 获取当前日期时间对象
	 * @return 当前日期时间对象
	 * 2017年12月13日
	 * @author MBG
	 */
	public synchronized SDate date() {
		return new SDate();
	}

	/**
	 * 将对象强制转换为字符串型的double数字
	 * @param douObj 带转换的对象
	 * @param xsd 小数点保留位数
	 * @return 转换后的数字字符串
	 * 2015年3月13日
	 * @author 马宝刚
	 */
	public String douStr(Object douObj,int xsd) {
    return SDouble.stringValueOf(douObj,xsd);
	}

	/**
	 * 将对象强制转换为字符串型的double数字
	 * @param douObj 带转换的对象
	 * @return 转换后的数字字符串
	 * 2015年3月13日
	 * @author 马宝刚
	 */
	public String douStr(Object douObj) {
		return SDouble.stringValueOf(douObj);
	}

	/**
	 * 整理浮点数小数点位数（小数点做四舍五入）
	 * @param dou 浮点数
	 * @param xsd 小数点
	 * @return 整理后的字符串
	 * 2016年11月15日
	 * @author MBG
	 */
	public String douStr(double dou,int xsd) {
		return SDouble.stringValueOf(dou,xsd);
	}

	/**
	 * 返回xx,xxx,xxx,xxx.xxxxx格式的Double字符串
	 * @param dou double值
	 * @param xsd 小数点
	 * @return    处理后的值
	 * 2019年5月15日
	 * @author MBG
	 */
	public String showDouble(Object dou,int xsd) {
		return (new SDouble(dou)).setPointDigit(xsd).getShowString();
	}

	/**
	 * 返回xx,xxx,xxx,xxx.xxxxx格式的Double字符串
	 * @param dou  double值
	 * @param xsd  小数点
	 * @param fgw  分割位数
	 * @return     处理后的值
	 * 2019年5月15日
	 * @author MBG
	 */
	public String showDouble(Object dou,int xsd,int fgw) {
		return (new SDouble(dou)).setPointDigit(xsd).getShowString(fgw);
	}

	/**
	 * 整理浮点数小数点位数（不做四舍五入，只截取小数点位数）
	 * @param dou 浮点数
	 * @param xsd 小数点
	 * @return 整理后的字符串
	 * 2019年05月14日
	 * @author MBG
	 */
	public String cutDou(double dou,int xsd) {
		return SDouble.stringValueOf(dou,xsd,true);
	}

	/**
	 * 将对象强制转换为double类型
	 * @param douObj 待转换的对象
	 * @param xsd 小数点保留位数
	 * @return 转换后的double类型
	 * 2015年3月13日
	 * @author 马宝刚
	 */
	public double dou(Object douObj,int xsd) {
    return SDouble.valueOf(douObj,xsd);
	}

	/**
	 * 将对象强制转换为double类型
	 * @param douObj 待转换的对象
	 * @return 转换后的double类型
	 * 2015年3月13日
	 * @author 马宝刚
	 */
	public double dou(Object douObj) {
    return SDouble.valueOf(douObj);
	}

	/**
	 * 将对象强制转换为布尔型
	 * @param booObj 待转换的对象
	 * @return 转换后的布尔型
	 * 2015年3月13日
	 * @author 马宝刚
	 */
	public boolean boo(Object booObj) {
    return SBoolean.valueOf(booObj);
	}

	/**
	 * 将对象强制转换成整型
	 * @param intObj 待转换的对象
	 * @return 转换后的整型变量
	 * 2015年3月13日
	 * @author 马宝刚
	 */
	public int sint(Object intObj) {
    return SInteger.valueOf(intObj);
	}

	/**
	 * 将对象强制转换成整型
	 * @param intObj      待转换的对象
	 * @param defValue  默认值
	 * @return 转换后的整型变量
	 * 2017年7月26日
	 * @author MBG
	 */
	public int sint(Object intObj,int defValue) {
    return SInteger.valueOf(intObj,defValue);
	}

	/**
	 * 将对象强制转换成字符串
	 * @param strObj 待转换的对象
	 * @return 转换后的字符串
	 * 2015年3月13日
	 * @author 马宝刚
	 */
	public String str(Object strObj) {
    return SString.valueOf(strObj);
	}

	/**
	 * 将对象强制转换成字符串
	 * @param strObj     待转换的对象
	 * @param defValue 默认值
	 * @return 转换后的字符串
	 * 2015年3月13日
	 * @author 马宝刚
	 */
	public String str(Object strObj,String defValue) {
		return SString.valueOf(strObj,defValue);
	}

	/**
	 * 将对象强制转换为去掉左右空格的字符串
	 * @param strObj 待转换对象
	 * @return       去掉左右空格的字符串
	 * 2018年7月5日
	 * @author MBG
	 */
	public String strm(Object strObj) {
		return SString.trimValueOf(strObj);
	}

	/**
	 * 将对象强制转换为去掉左右空格的字符串
	 * @param strObj   待转换对象
	 * @param defValue 默认值
	 * @return         去掉左右空格的字符串或默认值
	 * 2018年7月5日
	 * @author MBG
	 */
	public String strm(Object strObj,String defValue) {
		return SString.trimValueOf(strObj,defValue);
	}

	/**
	 * 注意：慎重使用，因为可能会取出意想不到的值
	 * 如果出现取值错误，请打开页面控制台日志，查看输出的日志
	 * 将参数放到当前线程容器中
	 * @param key 参数主键
	 * @param value 参数值
	 * 2015年4月2日
	 * @author 马宝刚
	 */
	public void threadPut(String key,Object value) {
    //日志实在太频繁，屏蔽掉
    //debug("$$$$ThreadSession$$$$ put:["+key+"] value:["+value+"]");
    ThreadSession.put(key,value);
	}

	/**
   * 注意：慎重使用，因为可能会取出意想不到的值
   * 如果出现取值错误，请打开页面控制台日志，查看输出的日志
	 * 从线程容器中获取指定参数
	 * @param key 参数主键
	 * @return 参数值
	 * 2015年4月2日
	 * @author 马宝刚
	 */
	public Object threadGet(String key) {
    //获取返回值
    Object reObj = ThreadSession.get(key);
    //日志实在太频繁，屏蔽掉
    //debug("$$$$ThreadSession$$$$ get:["+key+"] value:["+reObj+"]");
    return reObj;
	}

	/**
	 * 本线程是否输出日志（不包含错误日志）
	 * @param isOut 是否输出日志
	 * 2015年10月23日
	 * @author 马宝刚
	 */
	public void outLog(boolean isOut) {
    if(isOut) {
      ThreadSession.put("_nolog_","0");
    }else {
      ThreadSession.put("_nolog_","1");
    }
	}

	/**
	 * 本线程是否输出日志
	 * @return 本线程是否输出日志
	 * 2016年11月4日
	 * @author MBG
	 */
	public boolean outLog() {
		return !boo(ThreadSession.get("_nolog_"));
	}

	/**
	 * 打印异常信息（因为方法名太长记不住，就给简化了一下）
	 * @param e 异常信息
	 * 2016年11月8日
	 * @author MBG
	 */
	public void pe(Exception e) {
		e.printStackTrace();
	}

  /**
   * 获取当前日期时间（手动更新数据库时使用）
   * @return 当前日期时间
   * 2016年10月17日
   * @author MBG
   */
  public String dt() {
    return SDate.dt();
  }

  /**
   * 获取当前日期（手动更新数据库时使用）
   * @return 当前日期
   * 2016年10月17日
   * @author MBG
   */
  public String d() {
    return SDate.d();
  }

  /**
   * 返回缩减的日期
   * @return 缩减的日期字符串，格式：yyyymmdd
   * 2018年5月28日
   * @author MBG
   */
  public String thinDate() {
    return SDate.nowThinDate();
  }

  /**
   * 获取当前时间（手动更新数据库时使用）
   * @return 当前时间
   * 2016年10月17日
   * @author MBG
   */
  public String t() {
    return SDate.t();
  }

  /**
   * 获取当前时间戳（手动更新数据库时使用）
   * @return 当前时间戳
   * 2016年10月17日
   * @author MBG
   */
  public String ts() {
    return SDate.ts();
  }

  /**
   * 返回当前毫秒值
   * @return 当前毫秒值
   * 2016年10月17日
   * @author MBG
   */
  public long ms() {
    return System.currentTimeMillis();
  }

  /**
   * 获取配置文件中的参数  开头不需要加/
   * @param key 参数主键
   * @return 参数值，绝对不等于null
   * 2016年11月9日
   * @author MBG
   */
  public String p(String key) {
    return prop.getParameter(key);
  }

  /**
   * 获取指定节点的属性值
   * @param key            节点名
   * @param attributeName  节点中的属性名
   * @return               属性值
   * 2020年8月5日
   * @author MBG
   */
  public String pr(String key,String attributeName) {
    return prop.getParamterAttribute(key,attributeName);
  }

  /**
   * 获取配置文件中的参数  开头不需要加/
   * @param key 参数主键
   * @param defValue 默认值（如果配置文件中不存在该主键值，或者该主键值为空字符串时，返回默认值）
   * @return 参数值
   * 2019年7月16日
   * @author MBG
   */
  public String p(String key,String defValue) {
    String res = prop.getParameter(key);
    if(res.length()<1) {
      return defValue==null?"":defValue;
    }
    return res;
  }

  /**
   * 获取配置文件中的参数xml对象  开头不需要加/
   * @param key 参数主键
   * @return 参数xml对象
   * 2016年11月9日
   * @author MBG
   */
  public IViewHandler px(String key) {
    return prop.getParameterXml(key);
  }

  /**
   * 返回指定配置文件节点序列
   * 
   * <para>
   *  <ele>1</ele>
   *  <ele>2</ele>
   *  <ele>3</ele>
   * </para>
   * 
   * List<String> res = pl("para");
   * 
   * 返回的序列List：[1,2,3]
   * 
   * @param key 指定序列节点的主键
   * @return 指定节点序列
   * 2017年3月28日
   * @author MBG
   */
  public List<String> pl(String key){
    //构建返回值
    List<String> res = new ArrayList<String>();
    if(key==null || key.length()<1) {
      return res;
    }
    //指定节点的全部一级子节点序列
    List<IViewHandler> xmls = px(key).c();
    if(xmls!=null && xmls.size()>0) {
      for(IViewHandler ele:xmls) {
        res.add(ele.nt());
      }
    }
    return res;
  }

  /**
   * 返回指定配置文件节点的值
   * 
   * <para>
   *   <key1>val1</key1>
   *   <key2>val2</key2>
   *   <key3>val3</key3>
   * </para>
   * 
   * Map<String,String> res = pm("para");
   * 
   * 返回值：  key1=val1,key2=val2,key3=val3
   * 
   * @param key 节点名
   * @return    对应的参数值容器
   * 2019年7月16日
   * @author MBG
   */
  public Map<String,String> pm(String key) {
    //构建返回值
    Map<String,String> res = new HashMap<String,String>();
    if(key==null || key.length()<1) {
      return res;
    }
    //指定节点的全部一级子节点序列
    List<IViewHandler> xmls = px(key).c();
    if(xmls!=null && xmls.size()>0) {
      for(IViewHandler ele:xmls) {
        res.put(ele.nn(),ele.nt());
      }
    }
    return res;
  }

  /**
   * 获取配置文件中指定xml段指定的属性值
   * @param key            参数主键
   * @param attributeName  节点属性名
   * @return
   * 2016年11月23日
   * @author MBG
   */
  public String pa(String key,String attributeName) {
    //获取对应的配置信息块
    IViewHandler pXml = px(key);
    if(pXml==null) {
      return "";
    }
    return pXml.a(attributeName);
  }

  /**
   * 获取配置文件中指定xml段指定的属性值
   * @param key           参数主键
   * @param attributeName 对应参数段的属性名
   * @param defValue      默认值（如果配置文件中不存在该值，或者参数值为空字符串，则返回默认值）
   * @return              返回值
   * 2019年7月16日
   * @author MBG
   */
  public String pa(String key,String attributeName,String defValue) {
    String res = null; //构建返回值
    //获取对应的配置信息块
    IViewHandler pXml = px(key);
    if(pXml!=null) {
      res = pXml.a(attributeName);
    }
    if(res==null || res.length()<1) {
      return defValue==null?"":defValue;
    }
    return res;
  }

  /**
   * 获取其它xml配置文件xml对象
   * @param path 配置文件相对路径（相对于主配置文件路径）
   * @return 其它xml配置文件xml对象
   * 2016年11月9日
   * @author MBG
   */
  public IViewHandler pf(String path) {
    return prop.getConfigByPath(path);
  }

  /**
   * 判断参数如果为空或空字符串，则返回默认值
   * @param para 待判断值
   * @param defValue 默认值
   * @return 整理后的值
   * 2017年1月17日
   * @author MBG
   */
  public String def(String para,String defValue) {
    if(para==null || para.length()<1) {
      return defValue;
    }
    return para;
  }

  /**
   * HttpCall 调用目标接口（加上下划线是因为call方法名太常用了，容易被子类覆盖）
   * @param url       目标接口URL
   * @param data      提交数据，
   *                   可以为  aaa=111&bbb=222&ccc=333 方式，
   *                   也可以为json对象，
   *                   也可以是 IViewHandler 的xml对象
   * @param headerMap 报文头信息（包含会话信息）
   * @return          调用结果Json对象，返回值绝对不会为null，顶多是一个没有内容的json对象
   * 2017年4月3日
   * @author MBG
   */
  public Json _call(String url,Object data,Map<String,String> headerMap) {
    try {
      return HttpCall.call(url,data,headerMap);
    }catch(Exception e) {
      error("HttpCall Exception",e);
      return (new Json()).put("status","0").put("msg",e.toString());
    }
  }

  /**
   * HttpCall 调用目标接口（加上下划线是因为call方法名太常用了，容易被子类覆盖）
   * @param url   目标接口URL
   * @param data  提交数据，
   *               可以为  aaa=111&bbb=222&ccc=333 方式，
   *               也可以为json对象，
   *               也可以是 IViewHandler 的xml对象
   * @return      调用结果Json对象，返回值绝对不会为null，顶多是一个没有内容的json对象
   * 2017年4月3日
   * @author MBG
   */
  public Json _call(String url,Object data) {
    try {
      return HttpCall.call(url,data);
    }catch(Exception e) {
      error("HttpCall Exception",e);
      return (new Json()).put("status","0").put("msg",e.toString());
    }
  }

  /**
   * HttpCall 调用目标接口（加上下划线是因为call方法名太常用了，容易被子类覆盖）
   * @param url   目标接口URL
   * @return      调用结果Json对象，返回值绝对不会为null，顶多是一个没有内容的json对象
   * 2017年4月3日
   * @author MBG
   */
  public Json _call(String url) {
    try {
      return HttpCall.call(url,null);
    }catch(Exception e) {
      error("HttpCall Exception",e);
      return (new Json()).put("status","0").put("msg",e.toString());
    }
  }

  /**
   * HttpCall 调用目标接口（加上下划线是因为call方法名太常用了，容易被子类覆盖）
   * @param url   目标URL
   * @return      返回值
   * 2017年4月6日
   * @author MBG
   */
  public String _post(String url) {
    try {
      return HttpCall.post(url);
    }catch(Exception e) {
      error("HttpCall Exception",e);
      return "";
    }
  }

  /**
   * HttpCall 调用目标接口（加上下划线是因为call方法名太常用了，容易被子类覆盖）
   * @param url   目标URL
   * @param data  数据：  字符串（a=11&bb=22）  Json对象   IViewHandler对象（xml）
   * @return      返回值
   * 2017年4月6日
   * @author MBG
   */
  public String _post(String url,Object data) {
    try {
      return HttpCall.post(url,data);
    }catch(Exception e) {
      error("HttpCall Exception",e);
      return "";
    }
  }

  /**
   * HttpCall 调用目标接口（加上下划线是因为call方法名太常用了，容易被子类覆盖）
   * @param url       目标URL
   * @param data      数据：  字符串（a=11&bb=22）  Json对象   IViewHandler对象（xml）
   * @param headerMap 报文头信息（包含会话信息）
   * @return          返回值
   * 2017年4月6日
   * @author MBG
   */
  public String _post(String url,Object data,Map<String,String> headerMap) {
    try {
      return HttpCall.post(url,data,headerMap);
    }catch(Exception e) {
      error("HttpCall Exception",e);
      return "";
    }
  }

  /**
   * 将字符串进行URL（UTF-8）编码
   * @param src          源字符串
   * @return             处理后的字符串
   * 2017年4月10日
   * @author MBG
   */
  public String urlEnc(String src) {
    return StringUtil.getURLEncoding(src);
  }

  /**
   * 将字符串进行URL（UTF-8）解码
   * @param src          源字符串
   * @return             处理后的字符串
   * 2017年4月10日
   * @author MBG
   */
  public String urlDec(String src) {
    return StringUtil.getURLDecoding(src);
  }

  /**
   * 获取指定值的MD5值（小写）
   * @param value 待转换值
   * @return 转换后值
   * 2017年4月18日
   * @author MBG
   */
  public String md5(String value) {
    return MD5.md5(value);
  }

  /**
   * 返回文件处理类  相对根路径  WEB-INF 文件夹
   * @return 文件处理类
   * 2017年4月27日
   * @author MBG
   */
  public IFilesUtil file() {
    return filesUtil;
  }

  /**
   * 内部Base64解码（UTF-8编码）（替换了码表中的+和/符号，避免HTTP提交时转义报错）
   * @param str  待处理字符串
   * @return     处理后的字符串
   * 2017年4月27日
   * @author MBG
   */
  public String nd64(String str) {
    if(str==null) {
      return "";
    }
    if(str.startsWith("@b64@")) {
      return Base64.nativeD64(str.substring(5),"UTF-8");
    }
    return str;
  }

  /**
   * 内部Base64解码 （替换了码表中的+和/符号，避免HTTP提交时转义报错）
   * 注意：传入参数可能做了Base64加密，也可能没做，需要用前缀来判断
   * @param str            待处理字符串
   * @param encoding  编码格式
   * @return
   * 2017年4月27日
   * @author MBG
   */
  public String nd64(String str,String encoding) {
    if(str==null) {
      return "";
    }
    if(str.startsWith("@b64@")) {
      return Base64.nativeD64(str.substring(5),encoding);
    }
    return str;
  }

  /**
   * 内部Base64编码（UTF-8编码）（替换了码表中的+和/符号，避免HTTP提交时转义报错）
   * @param str  待处理字符串
   * @return       处理后的字符串
   * 2017年4月27日
   * @author MBG
   */
  public String ne64(String str) {
    if(str==null) {
      return "";
    }
    if(str.startsWith("@b64@") || str.length()<1) {
      return str;
    }
    return "@b64@"+Base64.nativeE64(str,"UTF-8");
  }

  /**
   * 内部Base64编码 （替换了码表中的+和/符号，避免HTTP提交时转义报错）
   * @param str  待处理字符串
   * @param encoding  编码格式
   * @return       处理后的字符串
   * 2017年4月27日
   * @author MBG
   */
  public String ne64(String str,String encoding) {
    if(str==null) {
      return "";
    }
    if(str.startsWith("@b64@") || str.length()<1) {
      return str;
    }
    return "@b64@"+Base64.nativeE64(str,encoding);
  }

  /**
   * Base64解码（UTF-8编码）
   * @param str  待处理字符串
   * @return     处理后的字符串
   * 2017年4月27日
   * @author MBG
   */
  public String d64(String str) {
    if(str==null) {
      return "";
    }
    if(str.startsWith("@b64@")) {
      return Base64.base64Decode(str,"UTF-8");
    }
    return str;
  }

  /**
   * Base64解码
   * 注意：传入参数可能做了Base64加密，也可能没做，需要用前缀来判断
   * @param str            待处理字符串
   * @param encoding  编码格式
   * @return
   * 2017年4月27日
   * @author MBG
   */
  public String d64(String str,String encoding) {
    if(str==null) {
      return "";
    }
    if(str.startsWith("@b64@")) {
      return Base64.base64Decode(str.substring(5),encoding);
    }
    return str;
  }

  /**
   * Base64编码（UTF-8编码）
   * @param str    待处理字符串
   * @return       处理后的字符串
   * 2017年4月27日
   * @author MBG
   */
  public String e64(String str) {
    if(str==null) {
      return "";
    }
    if(str.startsWith("@b64@") || str.length()<1) {
      return str;
    }
    return "@b64@"+Base64.base64Encode(str,"UTF-8");
  }

  /**
   * Base64编码，编码后再做URL格式编码
   * @param str   待处理字符串
   * @return      处理后的字符串
   * 2020年4月3日
   * @author MBG
   */
  public String ue64(String str) {
    return StringUtil.getURLEncoding(e64(str));
  }

  /**
   * Base64编码，编码后再做URL格式编码
   * @param str      待处理字符串
   * @param encoding 字符串编码格式
   * @return         处理后的字符串
   * 2020年4月3日
   * @author MBG
   */
  public String ue64(String str,String encoding) {
    return StringUtil.getURLEncoding(e64(str,encoding),encoding);
  }

  /**
   * Base64编码
   * @param str  待处理字符串
   * @param encoding  编码格式
   * @return       处理后的字符串
   * 2017年4月27日
   * @author MBG
   */
  public String e64(String str,String encoding) {
    if(str==null) {
      return "";
    }
    if(str.startsWith("@b64@") || str.length()<1) {
      return str;
    }
    return "@b64@"+Base64.base64Encode(str,encoding);
  }

  /**
   * 做Base64编码，并做URLEncode
   * @param str 待处理字符串
   * @return    处理后的编码
   * 2020年1月13日
   * @author MBG
   */
  public String p64(String str) {
    return p64(str,null);
  }

  /**
   * 做Base64编码，并做URLEncode
   * @param str 待处理字符串
   * @param enc 编码格式，默认UTF-8
   * @return    处理后的编码
   * 2020年1月13日
   * @author MBG
   */
  public String p64(String str,String enc) {
    if(str==null) {
      return "";
    }
    if(str.startsWith("@b64@") || str.length()<1) {
      return str;
    }
    if(enc==null || enc.length()<1) {
      enc = "UTF-8";
    }
    try {
      return URLEncoder.encode("@b64@"+Base64.base64Encode(str,enc),enc);
    }catch(Exception e) {
      e.printStackTrace();
    }
    return "@b64@"+Base64.base64Encode(str,enc);
  }

  /**
   * 返回相对于网站/WEB-INF 路径的绝对路径
   * @param subPath 相对路径（相对于网站 WEB-INF 路径）
   * @return 绝对路径
   * 2017年10月16日
   * @author MBG
   */
  public String infPath(String subPath) {
    if(subPath==null) {
      subPath = "";
    }
    return filesUtil.getAllFilePath(subPath);
  }

  /**
   * 返回/WEB-INF的绝对路径
   * @return  /WEB-INF的绝对路径
   * 2019年7月16日
   * @author MBG
   */
  public String infPath() {
    return _beanFactory.getWebInfPath();
  }

  /**
   * 返回相对于（上传、下载、备份）根文件夹的绝对路径
   * @param subPath 相对路径（相对于网站同级的file_sources路径）
   * @return 绝对路径
   * 2019年7月15日
   * @author MBG
   */
  public String sourcePath(String subPath) {
    if(subPath==null) {
      subPath = "";
    }
    return infPath(p("file_sources")+subPath);
  }

  /**
   * 返回（上传、下载、备份）根文件夹的绝对路径
   * @return 绝对路径
   * 2019年7月16日
   * @author MBG
   */
  public String sourcePath() {
    return infPath(p("file_sources"));
  }

  /**
   * 返回相对于上传根路径文件夹的绝对路径
   * @param subPath 相对于上传根路径文件夹的绝对路径
   * @return 绝对路径
   * 2019年7月15日
   * @author MBG
   */
  public String uploadPath(String subPath) {
    if(subPath==null) {
      subPath = "";
    }
    return infPath(p("file_sources")+"/upload/"+subPath);
  }

  /**
   * 返回上传根路径文件夹的绝对路径
   * @return 绝对路径
   * 2019年7月15日
   * @author MBG
   */
  public String uploadPath() {
    return infPath(p("file_sources")+p("upload_base_path"));
  }

  /**
   * 获取临时文件根路径
   * @return 临时文件根路径
   * 2018年12月7日
   * @author MBG
   */
  public String tmpPath() {
    return infPath(p("file_sources")+p("upload_temp_path"));
  }

  /**
   * 获取相对临时文件根路径的指定绝对路径
   * @param subPath  相对于临时文件根路径的相对路径
   * @return         对应的绝对路径
   * 2019年12月21日
   * @author MBG
   */
  public String tmpPath(String subPath) {
    if(subPath==null) {
      subPath = "";
    }else if(!subPath.startsWith("/")) {
      subPath = "/"+subPath;
    }
    return infPath(p("file_sources")+"/upload"+subPath);
  }

  /**
   * 创建临时文件对象
   * @param subFilePath 临时文件相对路径（相对临时文件夹）
   * @return 临时文件对象
   * 2019年1月2日
   * @author MBG
   */
  public File tmpFile(String subFilePath) {
    if(subFilePath!=null && !subFilePath.startsWith("/")) {
      subFilePath = "/"+subFilePath;
    }
    return SFilesUtil.createFile(tmpPath()+subFilePath);
  }

  /**
   * 返回相对于网站根路径的绝对路径
   * @param subPath 相对路径（相对于网站根路径）
   * @return 绝对路径
   * 2017年10月16日
   * @author MBG
   */
  public String path(String subPath) {
    if(subPath==null || subPath.length()<1) {
      subPath = "../";
    }else if(subPath.startsWith("/")) {
      subPath = ".."+subPath;
    }else {
      subPath = "../"+subPath;
    }
    return filesUtil.getAllFilePath(subPath);
  }

  /**
   * 返回网站根路径的绝对路径
   * @return 网站根路径的绝对路径
   * 2019年7月16日
   * @author MBG
   */
  public String path() {
    return infPath("../");
  }

  /**
   * 读取指定文件内容
   * @param subPath 文件相对路径（相对于网站根路径）
   * @param encoding 指定文件内容编码（默认UTF-8）
   * @return 文件内容
   * 2018年1月12日
   * @author MBG
   */
  public String fileContent(String subPath,String encoding) {
    if(encoding==null || encoding.length()<1) {
      encoding = "UTF-8";
    }
    try {
      return FileCopyTools.copyToString(new File(path(subPath)),encoding);
    }catch(Exception e) {
      e.printStackTrace();
    }
    return "";
  }

  /**
   * 读取指定文件内容（默认UTF-8编码）
   * @param subPath 文件相对路径（相对于网站根路径）
   * @return 文件内容
   * 2018年1月12日
   * @author MBG
   */
  public String fileContent(String subPath) {
    return fileContent(subPath,null);
  }

  /**
   * 获取当前会话中的用户姓名。 Session中的主键为：_user_name_
   * @return 用户姓名
   * 2018年7月3日
   * @author MBG
   */
  public String userName() {
    //从会话线程中获取动作上下文，如果存在的话
    IActionContext ac = (IActionContext)ThreadSession.get("_action_context");
    if(ac==null) {
      return "";
    }
    return str(ac.getSessionAttribute("_user_name_"));
  }

  /**
   * 获取当前会话中的用户数据主键。 Session中的主键为：_user_id_
   * @return 当前会话的用户数据主键
   * 2018年7月3日
   * @author MBG
   */
  public String userId() {
    //从会话线程中获取动作上下文，如果存在的话
    IActionContext ac = (IActionContext)ThreadSession.get("_action_context");
    if(ac==null) {
      return "";
    }
    return str(ac.getSessionAttribute("_user_id_"));
  }

	/**
	 * 将对象强制转换为字符串型long值
	 * @param lonObj 待转换的对象
	 * @return 字符串型long值
	 * 2018年7月31日
	 * @author MBG
	 */
	public String longs(Object lonObj) {
		return SLong.stringValueOf(lonObj);
	}

	/**
	 * 将对象强制转换为字符串型int值
	 * @param intObj 待转换的对象
	 * @return 字符串型int值
	 * 2018年7月31日
	 * @author MBG
	 */
	public String ints(Object intObj) {
		return SInteger.stringValueOf(intObj);
	}

	/**
	 * 注意：慎重使用，因为可能会取出意想不到的值
	 * 移除线程会话中指定主键值
	 * @param key 参数主键
	 * @return    已经被移除的参数值
	 * 2018年8月8日
	 * @author MBG
	 */
	public Object threadRemove(String key) {
		return ThreadSession.remove(key);
	}

	/**
	 * 返回当前时间毫秒值
	 * @return 时间毫秒值
	 * 2019年6月11日
	 * @author MBG
	 */
	public long curTime() {
		return System.currentTimeMillis();
	}
}
